using System;
using System.Collections.Generic;
using CLELCore;
using CLELCore.Assembly.CLELInstructions;
using CLELCore.Assembly.Descriptors;
using CLELCore.Assembly.Descriptors.ClassDescriptors;
using CLELCore.Assembly.BlobStreams.BlobInfo;
using CLELCore.Assembly.MethodHeaderDataSections;
using CLELCore.Assembly.MethodHeaderDataSections.MethodHeaderDataSectionRows;
using opt.Analysis.Set;
using opt.Analysis.Graph.ControlFlowGraph;
using opt.Tools.Branches;

namespace opt.lib.LocalCP
{
	public class LocalCP : IOptimization
	{
		public String Name
		{
			get
			{
				return "Local Constant Propagation";
			}
		}
		
		public LocalCP()
		{
		}

		public void DoOptimization(CLEL clel,CLELLogger log)
		{
			log.WriteLine("LocalCP: " + clel.Path+"\n");

			List<ClassDescriptor> class_descriptors = clel.GetInternalClasses();
			List<MethodDescriptor> method_descriptors;
			int i,class_count = class_descriptors.Count;
			int j,method_count;
			ClassDescriptor cd;
			TypeDefClassDescriptor tycd;
			MethodDescriptor md;
			MethodHeaderDataSection seh;
			List<CLELInstruction> code;
			MethodLocalsBlobInfo locals;
			GenKill gk;
			ControlFlowGraph<CFGArcInfo,CFGNodeInfo> cfg;
			ReachingDefinitions rd;
			List<BitSet> in_set;
			List<BitSet> stores;

			//for each class
			for(i = 0;i < class_count;i++)
			{
				cd = class_descriptors[i];
				if(cd.Type == ClassDescriptor.TYPEDEF_CLASSDESCRIPTOR)
				{
					tycd = (TypeDefClassDescriptor)cd;
					method_descriptors = tycd.Methods;
					method_count = method_descriptors.Count;
					//for each method
					for(j = 0;j < method_count;j++)
					{
						//if(count > 8) return;
						//if(i != 1 || j != 6) continue; //REMOVE ME
						md = method_descriptors[j];
						locals = md.Locals;
						code = md.GetMethodsCode();
					
						seh = md.SEHDataSection;
						if((seh == null) && (code != null))
						{
							gk = new GenKill(code,locals.Locals);
							cfg = new ControlFlowGraph<CFGArcInfo,CFGNodeInfo>(code);
							rd = new ReachingDefinitions(code,gk,cfg);
							in_set = rd.In;
							stores = gk.Stores;

							//Console.WriteLine("METHOD: "+i + " " + j);
							if(ProcessMethod(code,in_set,stores))
							{
								md.SetMethodsCode(code,locals);
							}
						}
					}
				}
			}
		}

		private bool ProcessMethod(List<CLELInstruction> code,List<BitSet> in_set,List<BitSet> stores)
		{
			UpdateBranchesAfterNoInstMoving update_branches = new UpdateBranchesAfterNoInstMoving(code);
			int i;
			int code_count = code.Count;
			CLELInstruction inst,prev_inst;
			int inst_num;
			int inst_index;
			BitSet inst_in_set;
			BitSet inst_inter;
			List<Int32> inst_inter_set;
			bool changed = false;
			for(i = 0;i < code_count;i++)
			{
				inst = code[i];
				inst_num = -1;
				if(LoadStoreUtilities.GetLoadLocalNum(inst,ref inst_num))
				{
					inst_in_set = in_set[i];
					inst_inter = stores[inst_num].Intersect(inst_in_set);
					inst_inter_set = inst_inter.GetNumberSet();
					if(inst_inter_set.Count == 1)
					{
						inst_index = inst_inter_set[0];
						if(inst_index >= 0)
						{
							prev_inst = code[inst_index-1];
							if(LoadStoreUtilities.IsLoadConstant(prev_inst))
							{
								//if prev inst is load const
								//copy to this instruction
								code[i] = prev_inst;
								changed = true;
								Console.WriteLine((inst_index-1) + " " + i);
							}
						}
					}
				}
			}
			if(changed)
				update_branches.UpdateOffsets();
			return changed;
		}

		/*
		private bool ProcessMethod(List<CLELInstruction> code,List<BitSet> in_set,List<BitSet> stores)
		{
			int code_count = code.Count;
			int i;
			CLELInstruction inst;
			int num;
			//BitSet temp;
			int num_count;
			int first_index;
			int num_temp;
			List<Int32> num_set;
			int j;
			//int inst_index;
			UpdateBranchesAfterNoInstMoving update_branches = new UpdateBranchesAfterNoInstMoving(code);
			bool changed = false;
			for(i = 0;i < code_count;i++)
			{
				inst = code[i];
				num = 0;
				if(LoadStoreUtilities.GetLoadLocalNum(inst,ref num))
				{
					temp = stores[num].Intersect(in_set[i]);
					num_set = temp.GetNumberSet();
					if(num_set.Count == 1)
					{
						inst_index = num_set[0];
						if(inst_index > 0)
						{
							prev_inst = code[inst_index-1];
							if(LoadStoreUtilities.IsLoadConstant(prev_inst))
							{
								//if prev inst is load const
								//copy to this instruction
								code[i] = prev_inst;
								changed = true;
							}
						}
					}
				}
			}
			if(changed)
				update_branches.UpdateOffsets();
			return changed;
		}
		*/
	}
}
